import XLSX from "xlsx-js-style";
/*
         == 导出封装 == 
         数据量范围：<50000条即可
         参数1(_data)：数据
            格式:[{},{},...]     
         参数2(_title)：标题
            {title: 标题名称, rowspan: 所占行数,默认2行}
         参数3(_changeData)：表头
            字段对应的名称{字段:名称,...}, 
            也是展示的顺序
         参数4(_leftRow)：左边合并栏
            左边栏需要合并的列索引 [0,...],可以嵌套合并
            默认[0]
         参数5(_rightRow)：右边汇总栏
            右边汇总栏 {需要汇总列索引: "表头名称",...}
            会追加到 _changeData参数3 后面
         参数6(_filterData)：筛选
            筛选规则 { 需要筛选的列索引: { type: (0小于,1等于,2大于), value: 临界值},... }
            默认：满足条件的单元格背景色 ‘亮绿色’
         参数7(_orderType)：排序
            {字段名: 1(1降序 2升序),...}
            类似于 mysql的多字段排序
         参数8(_rowHeight): 行高
            20   height默认20 
         参数9(_colWidth): 列宽
           {列索引: 100,...}
           默认每个列的宽都是 100
         参数10(_colStyle): 表头的单元格样式
           默认：内容行的样式继承表头的
         参数11(_isPool)：将汇总栏再次汇总
           是否在末尾加上汇总列：true/false
           会将 参数5(_rightRow)中的进一步进行汇总
           在末尾 合并2行展示，红色的字体颜色
       */
// == 封装 ， 基于array数据, _开头的变量 表示 参数
let writeFun_array = (
  __data,
  __title,
  __changeData,
  __leftRow,
  __rightRow,
  __filterData,
  __orderType,
  __rowHeight,
  __colWidth,
  __colStyle,
  __isPool
) => {
  console.log(1111);
  // 字段:id, name(名称), pidname(上级名称), starttime(创建时间), customnum(客户数量)
  // let _data = makeTableData(100);  // ------- 参数
  let _data = __data || [];
  // 左边需要合并的字段索引， 0对应_changeData的pidname
  // let _leftRow = [0, 1, 2, 3];   // ------- 参数
  let _leftRow = __leftRow || [0];
  let _leftRowFlag = (_leftRow || []).length;
  // 右边需要汇总的字段索引和名称  name对应1索引 customnum对应3索引
  // let _rightRow = { '1': '角色汇总', '3': '客户汇总' };   // ------- 参数
  let _rightRow = __rightRow || {};
  let _rightRowFlag = Object.keys(_rightRow || {}).length;
  // 筛选的规则 - 列字段对应规则 {列索引:{type:(0小于,1等于,2大于), value:临界值}, ...}
  // let _filterData = { '3': { type: 2, value: 5 } };    // ------- 参数
  let _filterData = __filterData || {};
  // 排序规则 - {字段名: 1降序 2升序,...}
  // let _orderType = { customnum: 1 };   // ------- 参数
  let _orderType = __orderType || {};
  // 行高 - 不写默认25
  let _rowHeight = __rowHeight || 25; // ------- 参数
  let rowHeights = [];
  // 列宽 - 不写默认80
  // let _colWidth = { '2': 160 };   // ------- 参数
  let _colWidth = __colWidth || {};
  let colWidths = [];
  // 标题的内容和占行
  // let _title = { title: '', rowspan: 3 };   // ------- 参数
  let _title = __title || {};
  // 将json数据处理为array   // ------- 参数
  // let _changeData = { pidname: '名称', name: '下级平台', starttime: '创建时间', customnum: '客户数量' };
  let _changeData = __changeData || {};
  // 单元格的样式 - 默认:所有边框 + 居中，根据表头的样式来 {title(标题):{}, titleHeader(表头):{} data(内容列):{列索引:{样式})}
  let _colStyle = __colStyle || {
    // ------- 参数
    title: {
      font: { bold: true, sz: 18 },
      alignment: { horizontal: "center", vertical: "center", wrapText: true },
    },
    titleHeader: {
      font: {
        bold: true,
        sz: 13,
      },
      border: {
        top: {
          style: "thin",
        },
        right: {
          style: "thin",
        },
        bottom: {
          style: "thin",
        },
        left: {
          style: "thin",
        },
      },
      alignment: { horizontal: "center", vertical: "center", wrapText: true },
    },
    data: {},
  };
  // 是否在末尾展示汇总行
  let _isPool = __isPool; // ------ 参数
  // --- 默认的单元格样式
  Object.keys({ ..._changeData, ..._rightRow }).forEach((item, i) => {
    _colStyle.data[i] = {
      font: { sz: 12 },
      border: {
        top: { style: "thin" },
        right: { style: "thin" },
        bottom: { style: "thin" },
        left: { style: "thin" },
      },
      alignment: { horizontal: "center", vertical: "center", wrapText: true },
      fill: { fgColor: { rgb: "FFFFFF" } },
    };
  });
  // 列的长度
  let col_length = Object.keys(_changeData).length + _rightRowFlag;
  let data = orderToorder(_data, _orderType); // 排序
  data = jsonToarray(data, _changeData, _rightRow);
  // 单元格的样式
  let allStyle = [];
  // 合并的单元格
  let allSpan = [];
  // 公共使用的变量名
  let publicN = "";
  // 当前的行索引 - 也可用作总行数的统计
  let row_now = 0;

  console.log(data);

  // --- 标题数据处理 --- //
  /*
        是否有标题，标题占的行数
    */
  let title = { rowspan: 2, ..._title }; // 标题默认 占两行
  if (title.title && title.rowSpan > 0) {
    // 设置标题样式表
    allStyle.push([0, 0, { ..._colStyle["title"] }]);
    // 合并值
    allSpan.push({
      s: { r: 0, c: 0 },
      e: { r: Number(title.rowspan) - 1, c: col_length - 1 },
    });
    // 添加值
    publicN = [];
    for (let i = 0; i < title.rowspan; i++) {
      publicN.push(new Array(col_length).fill(""));
    }
    publicN[0][0] = title.title;
    data.splice(0, 0, ...publicN);
    row_now += Number(title.rowspan);
  }
  // ----- end ----- //

  // --- 表头处理 --- //
  // === 设置样式即可
  for (let i = 0; i < col_length; i++) {
    allStyle.push([row_now, i, { ..._colStyle["titleHeader"] }]);
  }
  row_now++;

  // --- 内容处理 --- //
  // === 将数据提取
  let arrdata = JSON.parse(JSON.stringify(data));
  data = arrdata.slice(0, row_now);
  let datajson = {}; // 平铺的数据, {第一条列字段值:[[],...]}, 没有则默认首页
  let leftRow = {}; // 左合并的列索引操作 索引 {'1':{text: ,s:{r,c},e:{r,c}},...}
  let rightRow = {}; // 右汇总的列索引数值 {'1':值}
  let rightRows = {}; // 右汇总的总数 {'1':值}
  // 平铺数据获取
  arrdata.slice(row_now).forEach((item) => {
    // item = []
    if (!datajson[item[0]]) {
      datajson[item[0]] = [];
    }
    datajson[item[0]].push(item);
  });
  arrdata = arrdata.slice(0, row_now);

  // 转换为json处理
  for (let item in datajson) {
    // item  = [[],...]
    // 初始化数据
    item = datajson[item];
    leftRow = {};
    rightRow = {};

    if (item.length > 1) {
      // 是否有左合并设置第一个左合并
      if (_leftRowFlag > 0) {
        allSpan.push({
          s: { r: row_now, c: 0 },
          e: { r: row_now + item.length - 1, c: 0 },
        });
      }
      // 判断是否有右汇总
      if (_rightRowFlag > 0) {
        for (let i = _rightRowFlag; i > 0; i--) {
          allSpan.push({
            s: { r: row_now, c: col_length - i },
            e: { r: row_now + item.length - 1, c: col_length - i },
          });
        }
      }
    }
    item.forEach((item0) => {
      // item0 = []
      item0.forEach((item1, i1) => {
        // item1 = 值, i1列索引
        // 是否有筛选 - 默认背景填充黄色
        publicN = { fill: { fgColor: { rgb: "FFFFFF" } } };
        if (_filterData[i1]) {
          if (
            (_filterData[i1]["type"] == 0 &&
              _filterData[i1]["value"] > item1) ||
            (_filterData[i1]["type"] == 1 &&
              _filterData[i1]["value"] == item1) ||
            (_filterData[i1]["type"] == 2 && _filterData[i1]["value"] > item1)
          ) {
            publicN = { fill: { fgColor: { rgb: "00FF00" } } };
          }
        }
        // 设置单元格样式
        allStyle.push([row_now, i1, { ..._colStyle["data"][i1], ...publicN }]);
        // 是否有左合并, 前面已经做过一次合并
        if (_leftRowFlag > 1) {
          _leftRow.slice(1).forEach((item2) => {
            // item2 =列索引
            if (item2 == i1) {
              // 需要判断下一个来添加 leftRow
              // 判断是否有缓存
              if (leftRow[i1]) {
                // 判断值是否相等
                if (leftRow[i1]["text"] == item1) {
                  // 相等, e:{r++,c}
                  leftRow[i1]["e"]["r"]++;
                } else {
                  // 不相等，判断是否存储该合并
                  if (leftRow[i1]["e"]["r"] - leftRow[i1]["s"]["r"] > 0) {
                    allSpan.push({
                      s: { ...leftRow[i1]["s"] },
                      e: { ...leftRow[i1]["e"] },
                    });
                  }
                  // 更改
                  leftRow[i1]["text"] = item1;
                  leftRow[i1]["s"] = { r: row_now, c: i1 };
                  leftRow[i1]["e"] = { r: row_now, c: i1 };
                }
              } else {
                // 添加
                leftRow[i1] = {};
                leftRow[i1]["text"] = item1;
                leftRow[i1]["s"] = { r: row_now, c: i1 };
                leftRow[i1]["e"] = { r: row_now, c: i1 };
              }
            }
          });
        }
        // 是否有右汇总 - 需要判断是否是字符串， 字符串则统计个数，整数则统计数量
        if (_rightRowFlag && _rightRow[i1]) {
          if (!rightRow[i1]) {
            rightRow[i1] = 0;
          }
          if (!rightRows[i1]) {
            rightRows[i1] = 0;
          }
          if (typeof item1 == "number") {
            // 数量
            rightRow[i1] += Number(item1);
            rightRows[i1] += Number(item1);
          } else {
            // 个数
            rightRow[i1]++;
            rightRows[i1]++;
          }
        }
      });
      // 行数累加
      row_now++;
    });
    // 判断左合并，防止都需要合并的时候循环中无法判断 leftRow
    for (let item0 in leftRow) {
      if (leftRow[item0]["e"]["r"] - leftRow[item0]["s"]["r"] > 0) {
        allSpan.push({
          s: { ...leftRow[item0]["s"] },
          e: { ...leftRow[item0]["e"] },
        });
      }
    }
    // 判断右汇总是否需要添加值
    if (Object.keys(rightRow).length > 0) {
      let i = _rightRowFlag;
      for (let item0 in rightRow) {
        item[0][col_length - i] = rightRow[item0];
        i--;
      }
    }
    // 添加数据
    arrdata = [...arrdata, ...item];
  }
  // 添加总汇总的值 -- 背景 亮绿色，合并2行
  if (_isPool && Object.keys(rightRows).length > 0) {
    allSpan.push({
      s: { r: row_now, c: 0 },
      e: { r: row_now + 1, c: col_length - 1 },
    });
    // 设置样式
    allStyle.push([
      row_now,
      0,
      {
        font: { bold: true, sz: 14, color: { rgb: "FF0000" } },
        alignment: { horizontal: "center", vertical: "center", wrapText: true },
      },
    ]);
    publicN = "";
    for (let item0 in rightRows) {
      // item0=列索引
      publicN = "; " + _rightRow[item0] + ": " + rightRows[item0] + publicN;
    }
    arrdata.push([publicN.substr(2)]);
  }
  row_now += 2;
  data = JSON.parse(JSON.stringify(arrdata));

  // 设置行高和列宽的样式
  for (let i = 0; i < col_length; i++) {
    colWidths.push({
      wpx: _colWidth[i] || 100,
    });
  }
  for (let i = 0; i < row_now; i++) {
    rowHeights.push({
      hpx: _rowHeight,
    });
  }

  // ===== 生成 xlsx
  let ws = XLSX.utils.aoa_to_sheet(data); // 将数据数组转换为工作表对象
  // == 样式应用 == //
  // - 单元格样式
  allStyle.forEach(([row, cell, style]) => {
    let cellRef = XLSX.utils.encode_cell({ c: cell, r: row });
    if (!ws[cellRef]) ws[cellRef] = { t: "z" }; // 确保单元格存在
    ws[cellRef].s = style; // 设置样式
  });
  // - 列宽
  ws["!cols"] = colWidths;
  // - 行高
  ws["!rows"] = rowHeights;
  // - 合并单元格
  ws["!merges"] = allSpan;
  // 创建工作簿并添加工作表
  let wb = XLSX.utils.book_new();
  XLSX.utils.book_append_sheet(wb, ws, "Sheet1");

  // 写入文件
  let fileName = _title["title"] || new Date().getTime();
  XLSX.writeFile(wb, fileName + ".xlsx");

  /*
     合并规则：基于表头的多行自定义操作
       只要为空则表示需要合并, 需要考虑 跨行合并和跨列合并，所以先要检查行(深度)，再检查列(横向)
       --- 可制作 前端生成表头和样式的规则，将规则存入数据库 {title：{样式，重点-所占行数}, titleHeader：{样式，重点-所占行数}}，一定要存所占行数，要不然处理不了。
    */
};
/*
            ---- 将长度转换为列名称 
            参数1：列长度(0开始)，也就是字段的索引 
            参数2：转换的开始字符，因为xlsx的从A开始 列名称规则：A,B,....AA,,...BA,...AAA
            参数3：步长, 转换字符的规律
        */
let asciiToConname = (colNum, change, range) => {
  let coln = "",
    colyu = colNum % range,
    _A = change.charCodeAt();
  while (Math.floor(colNum / range) > 0) {
    colyu = colNum % range;
    colNum = Math.floor(colNum / range);
    coln += String.fromCharCode(colNum - 1 + _A);
  }
  return (coln += String.fromCharCode(colyu + _A));
};
/*
            ---- 将json数据转换为有顺序的array数据, 并添加表头，xlsx加载的array数据类型
            参数1：json数据
            参数2: 字段对应的名称 {字段名: 名称},后面的数据也会根据这个排序,如果没值则按默认的，有值则固定
            参数3: 综合数据的长度，在后面需要加入综合数据字段名称，用来对于某个字段做汇总使用
        */
let jsonToarray = (d, changeType, synData) => {
  try {
    let data = d || [],
      cdata = changeType || {},
      sdata = synData || {},
      sdatalen = Object.keys(sdata).length,
      arrs = [];
    if (d.length == 0) {
      return [];
    }
    // 删除表头在数据中没有的字段
    if (Object.keys(cdata).length > 0) {
      // 固定这个值展示
      for (let item in data[0]) {
        cdata[item] ? "" : delete cdata[item];
      }
    }

    data.forEach((item, i) => {
      // item = {}
      arrs[i] = [];
      // 排除 内容中为空的值，防止传入的排序的值多余数据中的值
      for (let item1 in item) {
        item[item1] || item[item1] === 0 ? "" : (item[item1] = " ");
      }
      // 上面的将 为空的值换为 ' ', 所以可以做字段校验 delete无需的字段
      for (let item2 in cdata) {
        item[item2] || item[item2] === 0 ? arrs[i].push(item[item2]) : "";
      }
      if (sdatalen > 0) {
        arrs[i].splice(arrs[i].length, 0, ...new Array(sdatalen).fill(0));
      }
    });
    arrs.unshift([...Object.values(cdata), ...Object.values(sdata)]);
    return arrs;
  } catch (e) {
    return [];
  }
};
/*
            ---- 将json数据转换为有顺序的array数据, 并添加表头，xlsx加载的array数据类型
            参数1：json数据 - 树形数据
            参数2: 总表头 字段对应的名称 {字段名: 名称},后面的数据也会根据这个排序,如果没值则按默认的，有值则固定
            参数3：子级表头 字段对应的名称 {字段名: 名称},后面的数据也会根据这个排序,如果没值则按默认的，有值则固定
          注意: 子级的数据中 需要前面加空格(前面)和加入序号
        */
let jsonToarray_tree = (d, parentName, sonName) => {
  try {
    let data = d || [],
      cdata = parentName || {},
      sdata = sonName || {},
      arrs = [];
    if (d.length == 0) {
      return [];
    }

    // 删除表头在数据中没有的字段
    if (Object.keys(cdata).length > 0) {
      // 固定这个值展示
      for (let item in data[0]) {
        cdata[item] ? "" : delete cdata[item];
      }
    }

    // 删除子表头在数据中没有的字段
    for (let item of data) {
      if (item.children && item.children.length > 0) {
        for (let item1 in item.children[0]) {
          sdata[item1] ? "" : delete sdata[item1];
        }
        break;
      }
    }
    let rnum = 0;
    data.forEach((item, i) => {
      // item = {}
      arrs[rnum] = [];
      // 排除 内容中为空的值，防止传入的排序的值多余数据中的值
      for (let item1 in item) {
        item[item1] || item[item1] === 0 ? "" : (item[item1] = " ");
      }
      // 上面的将为空的值换为 ' '
      for (let item1 in cdata) {
        item[item1] || item[item1] === 0 ? arrs[rnum].push(item[item1]) : "";
      }
      // 判断有没有子级
      if (item.children && item.children.length > 0) {
        // 添加子级表头
        rnum++;
        arrs[rnum] = ["", "序号", ...Object.values(sdata)];
        item.children.forEach((item1, i1) => {
          rnum++;
          // 注意：需要补充 缩进空格和序号
          arrs[rnum] = ["", i1 + 1];
          // 是否在 sdata中
          for (let item2 in sdata) {
            if (item1[item2] || item1[item2] === 0) {
              arrs[rnum].push(item1[item2]);
            } else {
              arrs[rnum].push("");
            }
          }
        });
      }
      rnum++;
    });
    // 添加总表头
    arrs.unshift([...Object.values(cdata)]);
    return arrs;
  } catch (e) {
    console.log(e);
    return [];
  }
};
/*
          排序 - 组合排序 - 原理:先里后外的升序排序
            参数1: [{},...], 数据
            参数2: 排序的规则  {字段名:1降序/2升序}
        */
let orderToorder = (data, orderFilter) => {
  let arr = data || [],
    f = orderFilter || {};
  if (arr.length == 0) {
    return {};
  }
  if (Object.keys(f).length == 0) {
    return arr;
  }
  let f1 = Object.keys(f).reverse();
  let asciix = 0,
    asciiy = 0;
  for (let item of f1) {
    arr.sort((x, y) => {
      asciix = 0;
      asciiy = 0;
      if (typeof y[item] == "string") {
        for (let i of x[item]) {
          asciix += i.charCodeAt();
        }
        for (let i of y[item]) {
          asciiy += i.charCodeAt();
        }
      } else {
        asciix = x[item];
        asciiy = y[item];
      }
      if (f[item] == 1) {
        // 降序
        return asciiy - asciix;
      } else {
        // 升序
        return asciix - asciiy;
      }
    });
  }
  return arr;
};
/*
          导出上下级树形数据
          参数1：__data 数据
            格式：[
                { ..., children:[...] },
                { ..., children:[...] },
                { ..., children:[...] },
                ...
            ]
            默认: []
          参数2：__parentName 父级字段名称
            格式: {字段:名称,...}
          参数3：__sonName 子级字段的名称
            格式：{字段:名称,...}
          参数4：__title 标题
            格式：{title:'', rowspan:所占行数} -- 默认{}
          参数5：__orderType 排序的规则
            格式：{父级字段名: 1降序 2升序} -- 默认 {}
          参数6：__rowHeight 行高
            格式： int  -- 默认25
          参数7：__colWidth 列宽
            格式：{列索引:宽度}  -- 默认 {}
        */
// == 封装，基于数据[{...children:[{},...]},{},...]上下级关系
function writeFun_array_tree(
  __data,
  __parentName,
  __sonName,
  __title,
  __orderType,
  __rowHeight,
  __colWidth
) {
  // 是否是上下级关系   -- 参数
  let _data = __data || [];
  // 父级字段转换的名字   --- 参数
  let _parentName = __parentName || {};
  // 子级字段转换的名字  --- 参数
  let _sonName = __sonName || {};
  // 标题的内容和占行
  let _title = __title || {}; // ------- 参数
  // 排序规则 - {字段名: 1降序 2升序,...}
  let _orderType = __orderType || {}; // ------- 参数
  // 行高 - 不写默认20
  let _rowHeight = __rowHeight || 25; // ------- 参数
  let rowHeights = [];
  // 列宽 - 不写默认80
  let _colWidth = __colWidth || {}; // ------- 参数
  let colWidths = [];
  // 单元格的样式 - 默认:所有边框 + 居中，根据表头的样式来 {title(标题):{}, titleHeader(表头):{} data(内容列):{列索引:{样式})}
  let _colStyle = {
    // ------- 参数
    title: {
      font: { bold: true, sz: 18 },
      alignment: { horizontal: "center", vertical: "center", wrapText: true },
    },
    titleHeader: {
      font: {
        bold: true,
        sz: 13,
      },
      border: {
        top: {
          style: "thin",
        },
        right: {
          style: "thin",
        },
        bottom: {
          style: "thin",
        },
        left: {
          style: "thin",
        },
      },
      alignment: { horizontal: "center", vertical: "center", wrapText: true },
    },
    data: {},
  };
  // 列的长度
  let col_length =
    Object.keys(_parentName).length > Object.keys(_sonName).length + 2
      ? Object.keys(_parentName).length
      : Object.keys(_sonName).length + 2;
  // --- 默认的单元格样式
  new Array(col_length).fill(0).forEach((item, i) => {
    _colStyle.data[i] = {
      font: { sz: 12 },
      border: {
        top: { style: "thin" },
        right: { style: "thin" },
        bottom: { style: "thin" },
        left: { style: "thin" },
      },
      alignment: { horizontal: "center", vertical: "center", wrapText: true },
      fill: { fgColor: { rgb: "FFFFFF" } },
    };
  });
  // 排序 - 按照排序规则
  let data = orderToorder(_data, _orderType);
  // 转换为array - xlsx加载的数据格式
  data = jsonToarray_tree(data, _parentName, _sonName);
  // 单元格的样式
  let allStyle = [];
  // 合并的单元格
  let allSpan = [];
  // 公共使用的变量名
  let publicN = "";
  // 当前的行索引 - 也可用作总行数的统计
  let row_now = 0;

  // --- 标题数据处理 --- //
  /*
        是否有标题，标题占的行数
    */
  let title = { rowspan: 2, ..._title }; // 标题默认 占两行
  if (title.title) {
    // 设置标题样式表
    allStyle.push([0, 0, { ..._colStyle["title"] }]);
    // 合并值
    allSpan.push({
      s: { r: 0, c: 0 },
      e: { r: Number(title.rowspan) - 1, c: col_length - 1 },
    });
    // 添加值
    publicN = [];
    for (let i = 0; i < title.rowspan; i++) {
      publicN.push(new Array(col_length).fill(""));
    }
    publicN[0][0] = title.title;
    data.splice(0, 0, ...publicN);
    row_now += Number(title.rowspan);
  }
  // ----- end ----- //

  // --- 表头处理 --- //
  // === 设置样式即可
  for (let i = 0; i < col_length; i++) {
    allStyle.push([row_now, i, { ..._colStyle["titleHeader"] }]);
  }
  row_now++;

  // --- 内容处理 --- //
  // 合并规则 - ''的为子级，需要合并
  let leftRow = []; // 左合并的列索引操作 索引 [{s:{r,c},e:{r,c}},...]

  let arrdata = JSON.parse(JSON.stringify(data)),
    datahead = arrdata.slice(0, row_now);
  data = arrdata.slice(row_now);
  let isnull = true; // push合并的状态
  // 转换为 array 处理
  for (let item of data) {
    // item  = []
    // 用户信息
    if (item[0]) {
      isnull = true;
    }
    // 追踪信息
    else {
      // 子级表头
      if (isnull) {
        leftRow.push({ s: { r: row_now, c: 0 }, e: { r: row_now, c: 0 } });
        publicN = { fill: { fgColor: { rgb: "00FF00" } } };
        isnull = false;
      } else {
        publicN = {};
        leftRow[leftRow.length - 1]["e"]["r"]++;
      }
    }
    item.forEach((item0, i1) => {
      // item0 = 值, i1列索引
      // 设置单元格样式
      if (i1 == 0) {
        // -- 空白
        allStyle.push([row_now, i1, { ..._colStyle["data"][i1] }]);
      } else {
        allStyle.push([row_now, i1, { ..._colStyle["data"][i1], ...publicN }]);
      }
    });
    // 行数累加
    row_now++;
  }
  data = [...datahead, ...data];

  // 筛选正确的左合并，防止都需要合并的时候循环中无法判断 leftRow
  for (let item0 of leftRow) {
    if (item0["e"]["r"] - item0["s"]["r"] > 0) {
      allSpan.push({ s: { ...item0["s"] }, e: { ...item0["e"] } });
    }
  }

  // 设置行高和列宽的样式
  for (let i = 0; i < col_length; i++) {
    colWidths.push({
      wpx: _colWidth[i] || 100,
    });
  }
  for (let i = 0; i < row_now; i++) {
    rowHeights.push({
      hpx: _rowHeight,
    });
  }

  // ===== 生成 xlsx
  let ws = XLSX.utils.aoa_to_sheet(data); // 将数据数组转换为工作表对象
  // == 样式应用 == //
  // - 单元格样式
  allStyle.forEach(([row, cell, style]) => {
    let cellRef = XLSX.utils.encode_cell({ c: cell, r: row });
    if (!ws[cellRef]) ws[cellRef] = { t: "z" }; // 确保单元格存在
    ws[cellRef].s = style; // 设置样式
  });
  // - 列宽
  ws["!cols"] = colWidths;
  // - 行高
  ws["!rows"] = rowHeights;
  // - 合并单元格
  ws["!merges"] = allSpan;
  // 创建工作簿并添加工作表
  let wb = XLSX.utils.book_new();
  XLSX.utils.book_append_sheet(wb, ws, "Sheet1");

  // 写入文件
  let fileName = _title["title"] || new Date().getTime();
  XLSX.writeFile(wb, fileName + ".xlsx");
}

export { writeFun_array, writeFun_array_tree };
